<?php
# ***** BEGIN LICENSE BLOCK *****
# This file is part of "myWiWall".
# Copyright (c) 2007-2008 CRP Henri Tudor and contributors.
# All rights reserved.
#
# "myWiWall" is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 as published by
# the Free Software Foundation.
# 
# "myWiWall" is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with "myWiWall"; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
# ***** END LICENSE BLOCK *****


/**
 * The UserInterface class depicts the access to persistent data relevant to the UI of a particular user.
 * It allows you to insert a widget in user's UI, to delete a widget from a user's UI, to retrieve the user's UI
 * in a json, xml or raw data format, etc ...
 * 
 * @author Laurent Haan <laurent.haan@gmail.com>
 * @author J�r�me Bogaerts <jerome.bogaerts@gmail.com>
 * @package mywiwall.models 
 *
 */
class UserInterface
{
	/**
	 * Inserts a new widget in a user's UI in the database.
	 *
	 * @param integer $userId The user's identifier.
	 * @param string $widgetId The widget's identifier.
	 * @param array $widgetStates A list of associative array containing each one the following entries : 'column', 'position', 'widgetid'.
	 */
	public static function insertWidget($userId, $widgetId, array $widgetStates)
	{
		$db = DbUtil::accessFactory();
		echo $userId;	
		$userId = $db->escape($userId);
		$widgetId = $db->escape($widgetId);
		
		if ($db->execute("INSERT INTO `interface` (`userid`, `widgetid`, `column`, `minimized`, `position`) VALUES (${userId}, '${widgetId}', 0, 0, 0)"))

			// Synchronize the interface.
			self::update($userId, $widgetStates);
		
		else
			// FIXME And why not let the application run ? It's not so bad ...
			throw new DBException(MwwException::MODEL, 'Unable to insert the widget in user\'s interface');
	}
	
	/**
	 * Deletes a widget in a user's UI in the database.
	 *
	 * @param integer $userId The user's identifier.
	 * @param string $widgetId The widget's identifier.
	 * @param array $widgetStates A list of associative array containing each one the following entries : 'column', 'position', 'widgetid'.
	 */
	public static function deleteWidget($userId, $widgetId, array $widgetStates)
	{
		// connexion and sanitization.
		$db = DbUtil::accessFactory();
		$userId = $db->escape($userId);
		$widgetId = $db->escape($widgetId);
		
		// remove widget from interface.
		$db->execute("DELETE FROM `interface` WHERE `userid` = ${userId} AND `widgetid` = '${widgetId}'");
		
		// remove user preferences.
		$db->execute("DELETE FROM `interface_preferences` WHERE `userid` = ${userId} AND `widgetid` = '${widgetId}'");
		
		self::update($userId, $widgetStates);
	}
	
	/**
	 * Minimizes a widget in a user's UI in the database.
	 *
	 * @param integer $userId The user's identifier.
	 * @param string $widgetId The widget's identifier.
	 */
	public static function minimizeWidget($userId, $widgetId)
	{		
		$db = DbUtil::accessFactory();
		$userId = $db->escape($userId);
		$widgetId = $db->escape($widgetId);
		
		$db->execute("UPDATE `interface` SET `minimized` = !`minimized` WHERE `userid` = ${userId} AND `widgetid` = '${widgetId}'");	
	}
	
	/**
	 * Updates a complete user's UI in the database.
	 *
	 * @param integer $userId The user's identifier.
	 * @param array $widgetStates A list of associative array containing each one the following entries : 'column', 'position', 'widgetid'.
	 */
	public static function update($userId, array $widgetStates)
	{
		$db = DbUtil::accessFactory();
			
		foreach ($widgetStates as $state)
		{
			$column 	= $state['column'];
			$position 	= $state['position'];
			$widgetId 	= $state['widgetid'];
			
			$db->execute("UPDATE `interface` SET `column` = ${column}, `position` = ${position} WHERE `userid` = ${userId} AND `widgetid` = '${widgetId}'");
		}
	}
	
	/**
	 * Retrieve a complete description of the user's UI in the requested format. This
	 * format can be 'json', 'xml', or 'raw' (raw data in an array).
	 *
	 * @param integer $userId The user's identifier.
	 * @param string $format The requested returned format. If it's not 'xml', 'json' or 'raw', this method will raise an exception.
	 * @return string The The complete description of the user's UI in the requested format.
	 */
	public static function retrieve($userId, $format = 'xml')
	{	
		$format = strtolower($format);
		if ($format != 'xml' && $format != 'json' && $format != 'raw')
			throw new BadArgumentException(MwwException::MODEL, 'Unknown output format in UserInterface::get. Only xml, json or raw is accepted.');
		
		// Widget array used for output generation.
		$widgets = array();	
			
		// DB access.
		$db = DbUtil::accessFactory();
		$userId = $db->escape($userId);
			
		$rs = $db->select("SELECT w.widgetid, i.column, i.minimized FROM users u, interface i, widgets w WHERE u.id = ${userId} AND i.userid = u.id AND w.widgetid = i.widgetid ORDER BY i.column ASC, i.position DESC");
			
		// Transforms the rowset in an array.	
		while ($rs->fetch())
		{
			
			$widgets[] = array('name' 					=> $rs->widgetid, 
							   'column' 				=> 'c_' . $rs->column, 
							   'minimized' 				=> $rs->minimized);
		}
		
		// Output in the requested format : xml, json or raw.
		$output = '';
		switch($format)
		{
			case 'json':
				$output = json_encode($widgets);
				break;
			
			case 'raw':
				$output = $widgets;
				break;
				
			case 'xml':
			default:
				header('Content-Type: text/xml; charset=iso-8859-1');
				$dom = new DOMDocument('1.0', 'iso-8859-1');
				$root = $dom->createElement('widgets');
				
				foreach($widgets as $widget)
				{
					$widgetNode = $dom->createElement('widget', $widget['name']);
					$widgetNode->setAttribute('column', $widget['column']);
					$widgetNode->setAttribute('minimized', $widget['minimized']);
					$root->appendChild($widgetNode);
				}
				
				$dom->appendChild($root);
				
				$output = $dom->saveXML();
				break;
		}
		
		return $output;
	}
	
	/**
	 * Retrieves the XML manifest of a widget for a given user. Be carefull, the returned
	 * manifest will not be exactly the same as the manifest deployed on the application server.
	 * Actually, the return manifest will be provided with the preferences relevant with a given user,
	 * and not the default values of the preferences as expected.
	 *
	 * @param integer $userId The user's identifier.
	 * @param string $widgetId The widget's identifier.
	 * @return string The requested manifest as a character stream.
	 */
	public static function retrieveWidgetManifest($userId, $widgetId)
	{
		// We load the document wich must be in the widgets direcory.
		try
		{
			$doc = new DOMDocument();
			$doc->preserveWhiteSpace = false;
			$doc->load('widgets/' . $widgetId . '/config.xml');
			
			if (VALIDATE_XML_FILE && !$doc->schemaValidate(XML_SCHEMA_FILE))
			{
				throw new XMLValidationException(MwwException::MODEL, XML_SCHEMA_FILE);
			}
			else
			{
				// Manifest loaded and validated.
				// Now we add it dynamic values relevant to the user preferences.
				$db = DbUtil::accessFactory();
				$userId = $db->escape($userId);
				$widgetId = $db->escape($widgetId);
					
				$prefs = $doc->getElementsByTagNameNS('http://palette.ercim.org/ns/', 'preference');
				foreach($prefs as $pref)
				{
					$name = $pref->attributes->getNamedItem('name')->value;
					
					$rs = $db->select("SELECT `value` FROM `interface_preferences` WHERE `widgetid` = '${widgetId}' AND `userid` = ${userId} AND `preference` = '${name}'");
					
					/* Read the the result set into an associative array */
					if ($rs->count() == 1)
					{
						$pref->setAttribute('value', $rs->value);
					}
					else
					{
						if ($pref->attributes->getNamedItem('default_value'))
							$pref->setAttribute('value', $pref->attributes->getNamedItem('default_value')->value);
						else
							$pref->setAttribute('value', '');
					}
				}
				
				return $doc->saveXML();
			}
			
		}
		catch (DOMException $e)
		{
			throw new FileException(MwwException::MODEL, 'Unable to load the manifest for widget '.$widgetId);
		}
	}
	
	/**
	 * Updates user preferences for a given widget. This update is made using
	 * a set of preferences for all the value-keys available in the widget's configuration.
	 * 
	 * Example of $preferences setting new values for keys 'host' and 'pictures' : 
	 * array ('host' => '127.0.0.1', 'picture' => 'http://www.gravatar.com/wanobi-obiken')
	 * 
	 * Example of $tags array containing the 'cool', 'gorgeous', 'master yoghurt' tags.
	 * array ('cool', 'gorgeous', 'master yoghurt');
	 *
	 * @param integer $userId The user identifier.
	 * @param string $wigetId The widget identifier.
	 * @param array $preferences The preferences to set to the widget for a given user. This array must be formatted as an associative array.
	 * @param array $tags The tags associated with the user. If there's no tags, just provide a empty array (example: array());
	 */
	public static function updateWidgetPreferences($userId, $widgetId, array $preferences, array $tags)
	{	

		$db 		= DbUtil::accessFactory();
		$userId 	= $db->escape($userId);
		$widgetId 	= $db->escape($widgetId);
		//$tags 		= $db->escape($tags);
		
		// Widget user preferences update.
		foreach ($preferences as $key => $value)	
			// FIXME The REPLACE keyword is not ANSI SQL compliant.
			$db->execute("REPLACE `interface_preferences` (`userid`, `widgetid`, `preference`, `value`) VALUES (${userId}, '${widgetId}', '${key}', '${value}')");
		
		
		// We have to handle new tags eventually added by the user during preferences edition.
		if (count($tags))
		{
			foreach($tags as $tag)
			{
				$rs = $db->select("SELECT label FROM tags WHERE label = '${tag}'");
				if(!$rs->count())
					$db->execute("REPLACE `tags` (`label`) VALUES ('${tag}')");		
				
				$db->execute("INSERT INTO `widget_tags` (`widget`, `tag`) VALUES ('${widgetId}', '${tag}')");
			}
		}
	}
	
	/**
	 * Makes effective a subscribing to a given widget for a particular user in the database. If
	 * the widget already exists in the user's UI, the call to this function will simply
	 * be ignored.
	 * 
	 * You have to know that if the widget is inserted in the user's UI, it will take place
	 * in the first column of the screen, at the first place (in other words: top-left on the screen).
	 *
	 * @param integer $userId The user's identifier in the database
	 * @param string $widgetId The widget's identifier to wich the user want to subscribe.
	 */
	public static function subscribe($userId, $widgetId)
	{
		// Test if the widget exists. Developpers please be carefull, there's a dependancy on /models/Widgets::isInstalled.
		if(!Widgets::isInstalled($widgetId))
			throw new ServiceException(MwwException::MODEL, "Unable to subscribe user '${userId}' to widget '${widgetId}'. The widget is not installed.");

		// Sanitization and database connexion.
		$db = DbUtil::accessFactory();
		$userId = $db->escape($userId);
		$widgetId = $db->escape($widgetId);	
			
		// Test if the widget needs to be added to the user interface.
		$rs = $db->select("SELECT * FROM interface WHERE userid = ${userId} AND widgetid = '${widgetId}'");
	
		if($rs->isEmpty())
		{
			// We can subscribe this user to the requested widget.
			// Developpers note : be careful the 'column' mysql keyword use in the following queries. Use brackets !
			$db->execute("UPDATE interface SET position = position + 1 WHERE userid = ${userId} AND `column` = 0");
			$db->execute("INSERT INTO interface (userid, widgetid, `column`, minimized, position) VALUES (${userId}, '${widgetId}', '0', '0', '0')");
		}
	}
}
?>